<template>
  <div class="virtual-table-container" ref="containerRef" @scroll="handleScroll">
    <!-- 固定表头 -->
    <table class="virtual-table-header" ref="headerTableRef">
      <thead>
        <tr>
          <th v-for="column in columns" :key="column.key" :style="{ width: column.width + 'px' }">
            {{ column.title }}
          </th>
        </tr>
      </thead>
    </table>

    <!-- 虚拟内容区域 -->
    <div class="virtual-table-content" ref="contentRef">
      <!-- 占位元素高度需要动态调整 -->
      <div class="virtual-placeholder" :style="{ height: placeholderHeight + 'px' }"></div>
      <table class="virtual-table-rows">
        <tbody>
          <tr v-for="(item, rowIndex) in visibleItems" :key="item.id || rowIndex" :style="{ height: rowHeight + 'px' }">
            <td v-for="column in columns" :key="column.key" :style="{ width: column.width + 'px' }">
              {{ getItemValue(item, column.key) }}
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>
  
  <script setup>
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'

const props = defineProps({
  data: {
    type: Array,
    required: true
  },
  columns: {
    type: Array,
    required: true
  },
  rowHeight: {
    type: Number,
    default: 40
  }
})

const containerRef = ref(null)
const headerTableRef = ref(null)
const contentRef = ref(null)
const visibleItems = ref([])
const startIndex = ref(0)
const endIndex = ref(0)
const offsetY = ref(0)

// 计算总高度
const totalHeight = computed(() => {
  return props.data.length * props.rowHeight
})

// 计算占位元素的动态高度
const placeholderHeight = computed(() => {
  return Math.max(0, totalHeight.value - offsetY.value)
})

// 获取单元格值
const getItemValue = (item, key) => {
  if (typeof key === 'function') {
    return key(item)
  }
  return item[key]
}

// 计算可视区域内的项目
const calculateVisibleItems = () => {
  if (!containerRef.value) return

  const container = containerRef.value
  const scrollTop = container.scrollTop
  const visibleHeight = container.clientHeight

  // 计算可见区域的起始和结束索引
  const visibleStartIndex = Math.floor(scrollTop / props.rowHeight)
  const visibleEndIndex = Math.ceil((scrollTop + visibleHeight) / props.rowHeight)

  // 添加缓冲区，提高滚动流畅度
  const buffer = 5
  startIndex.value = Math.max(0, visibleStartIndex - buffer)
  endIndex.value = Math.min(props.data.length, visibleEndIndex + buffer)

  // 更新可视项目
  visibleItems.value = props.data.slice(startIndex.value, endIndex.value)

  // 更新内容区域的偏移量
  offsetY.value = startIndex.value * props.rowHeight
  contentRef.value.style.transform = `translateY(${offsetY.value}px)`
}

// 处理滚动事件
const handleScroll = () => {
  calculateVisibleItems()
}

// 初始化
onMounted(() => {
  nextTick(() => {
    // 设置表头宽度与内容列宽度一致
    const headerCells = headerTableRef.value.querySelectorAll('th')
    const contentCells = contentRef.value.querySelectorAll('td')

    // 确保列宽一致
    if (headerCells.length > 0 && contentCells.length > 0) {
      columns.forEach((column, index) => {
        if (headerCells[index] && contentCells[index]) {
          headerCells[index].style.width = `${column.width}px`
          contentCells[index].style.width = `${column.width}px`
        }
      })
    }

    // 初始计算可见项目
    calculateVisibleItems()

    // 监听窗口大小变化，重新计算
    window.addEventListener('resize', calculateVisibleItems)
  })
})

// 监听数据变化
watch(
  () => props.data,
  () => {
    calculateVisibleItems()
  }
)

// 组件卸载时移除事件监听
onUnmounted(() => {
  window.removeEventListener('resize', calculateVisibleItems)
})
</script>
  
  <style scoped>
.virtual-table-container {
  position: relative;
  overflow: auto;
  height: 400px;
  border: 1px solid #e5e7eb;
}

.virtual-table-header {
  position: sticky;
  top: 0;
  background-color: #f9fafb;
  z-index: 1;
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

.virtual-table-content {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}

.virtual-placeholder {
  width: 100%;
}

.virtual-table-rows {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

.virtual-table-header th,
.virtual-table-rows td {
  padding: 8px 12px;
  border-right: 1px solid #e5e7eb;
  border-bottom: 1px solid #e5e7eb;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  box-sizing: border-box;
}

.virtual-table-header th:last-child,
.virtual-table-rows td:last-child {
  border-right: none;
}

.virtual-table-header th {
  font-weight: 600;
  text-align: left;
}

.virtual-table-rows tr:nth-child(even) {
  background-color: #f9fafb;
}

.virtual-table-rows tr:hover {
  background-color: #f3f4f6;
}
</style>  